package thread;

// 自己写一个阻塞队列
class MyBlockingQueue {
    // 有效值范围 [head, tail) tail 指向的是最后一个值的下一个
    private Integer[] elems = null;

    // 防止内存可见性、指令重排序等问题
    private volatile int size;
    private volatile int head;
    private volatile int tail;

    public MyBlockingQueue(int capacity) {
        elems = new Integer[capacity];
        head = 0;
        tail = 0;
        size = 0;
    }

    public void put(Integer elem) throws InterruptedException {
        // 多线程就要用到锁
        synchronized (this) {
            // 注意： 这里if最好用while 因为如果出来的时候依然需要阻塞 那么就还是要执行 wait
            while (size >= elems.length) {
                // 阻塞队列...
                this.wait();
            }
            elems[tail] = elem;
            tail++;
            if (tail == elems.length) {
                // 到末尾 重置为0
                tail = 0;
            }
            size++;
            // 通知 take 可以执行了（如果可以）
            this.notify(); // 不能 notifyAll 因为一个入队就只能有一个出队
        }
    }

    public synchronized Integer take() throws InterruptedException {
        while (size == 0) {
            // 阻塞队列...
            this.wait();
        }
        Integer elem = elems[head];

//        head++;
//        if (head == elems.length) {
//            head = 0;
//        }
        // 上面也可以写为这个 不过效率相对比较低 并且不易阅读
        head = (head + 1) % elems.length;
        size--;
        // 同理 通知 put
        this.notify();
        return elem;
    }
}


public class Demo31 {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue(1000);

        // 生产者
        Thread t1 = new Thread(() -> {
            int count = 0;
            while (true) {
                try {
                    System.out.println("生产：" + count);
                    queue.put(count);
                    count++;
//                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 消费者
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    int result = queue.take();
                    System.out.println("消费：" + result);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}
